﻿// Created by amoylel on 12/17/2018.
// Copyright (c) 2018 amoylel All rights reserved.

#ifndef AMO_STRING_ALGO_F04C5FE1_7DCD_4367_B76D_23905D0A7E27_HPP__
#define AMO_STRING_ALGO_F04C5FE1_7DCD_4367_B76D_23905D0A7E27_HPP__

#include <string>
#include <algorithm>
#include <utility>
#include <vector>
#include <regex>
#include <unordered_map>

namespace amo {
    namespace string_utils {
        static bool is_utf8(const char * string) {
            if (!string) {
                return false;
            }
            
            const unsigned char * bytes = (const unsigned char *)string;
            
            while (*bytes) {
                if ((// ASCII
                            // use bytes[0] <= 0x7F to allow ASCII control characters
                            bytes[0] == 0x09 ||
                            bytes[0] == 0x0A ||
                            bytes[0] == 0x0D ||
                            (0x20 <= bytes[0] && bytes[0] <= 0x7E)
                        )
                   ) {
                    bytes += 1;
                    continue;
                }
                
                if ((// non-overlong 2-byte
                            (0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
                            (0x80 <= bytes[1] && bytes[1] <= 0xBF)
                        )
                   ) {
                    bytes += 2;
                    continue;
                }
                
                if ((// excluding overlongs
                            bytes[0] == 0xE0 &&
                            (0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
                            (0x80 <= bytes[2] && bytes[2] <= 0xBF)
                        ) ||
                        (// straight 3-byte
                            ((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
                             bytes[0] == 0xEE ||
                             bytes[0] == 0xEF) &&
                            (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
                            (0x80 <= bytes[2] && bytes[2] <= 0xBF)
                        ) ||
                        (// excluding surrogates
                            bytes[0] == 0xED &&
                            (0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
                            (0x80 <= bytes[2] && bytes[2] <= 0xBF)
                        )
                   ) {
                    bytes += 3;
                    continue;
                }
                
                if ((// planes 1-3
                            bytes[0] == 0xF0 &&
                            (0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
                            (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
                            (0x80 <= bytes[3] && bytes[3] <= 0xBF)
                        ) ||
                        (// planes 4-15
                            (0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
                            (0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
                            (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
                            (0x80 <= bytes[3] && bytes[3] <= 0xBF)
                        ) ||
                        (// plane 16
                            bytes[0] == 0xF4 &&
                            (0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
                            (0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
                            (0x80 <= bytes[3] && bytes[3] <= 0xBF)
                        )
                   ) {
                    bytes += 4;
                    continue;
                }
                
                return false;
            }
            
            return true;
        }
        
        static bool is_utf8(const std::string& str) {
            return is_utf8(str.c_str());
        }
        
        static bool validate_utf8(const char *str, int len) {
            int i;
            int j;
            int codelen;
            int codepoint;
            const unsigned char *ustr = (const unsigned char *)str;
            
            if (!str) {
                return false;
            }
            
            if (len < 0 || len > 65536) {
                return false;
            }
            
            for (i = 0; i < len; i++) {
                if (ustr[i] == 0) {
                    return false;
                } else if (ustr[i] <= 0x7f) {
                    codelen = 1;
                    codepoint = ustr[i];
                } else if ((ustr[i] & 0xE0) == 0xC0) {
                    /* 110xxxxx - 2 byte sequence */
                    if (ustr[i] == 0xC0 || ustr[i] == 0xC1) {
                        /* Invalid bytes */
                        return false;
                    }
                    
                    codelen = 2;
                    codepoint = (ustr[i] & 0x1F);
                } else if ((ustr[i] & 0xF0) == 0xE0) {
                    // 1110xxxx - 3 byte sequence
                    codelen = 3;
                    codepoint = (ustr[i] & 0x0F);
                } else if ((ustr[i] & 0xF8) == 0xF0) {
                    // 11110xxx - 4 byte sequence
                    if (ustr[i] > 0xF4) {
                        /* Invalid, this would produce values > 0x10FFFF. */
                        return false;
                    }
                    
                    codelen = 4;
                    codepoint = (ustr[i] & 0x07);
                } else {
                    /* Unexpected continuation byte. */
                    return false;
                }
                
                /* Reconstruct full code point */
                if (i == len - codelen + 1) {
                    /* Not enough data */
                    return false;
                }
                
                for (j = 0; j < codelen - 1; j++) {
                    if ((ustr[++i] & 0xC0) != 0x80) {
                        /* Not a continuation byte */
                        return false;
                    }
                    
                    codepoint = (codepoint << 6) | (ustr[i] & 0x3F);
                }
                
                /* Check for UTF-16 high/low surrogates */
                if (codepoint >= 0xD800 && codepoint <= 0xDFFF) {
                    return false;
                }
                
                /* Check for overlong or out of range encodings */
                if (codelen == 2 && codepoint < 0x0080) {
                    return false;
                } else if (codelen == 3 && codepoint < 0x0800) {
                    return false;
                } else if (codelen == 4 && (codepoint < 0x10000 || codepoint > 0x10FFFF)) {
                    return false;
                }
            }
            
            return true;
        }
        
        static bool validate_utf8(const std::string& str) {
            return validate_utf8(str.c_str(), str.size());
        }
        
        
        static std::vector< std::string > split(const std::string& src,
                                                const std::string& sub) {
            std::string s = src;
            std::string delim = sub;
            
            std::vector<std::string> ret;
            size_t start = 0;
            
            if (s.empty()) {
                ret.push_back("");
                return ret;
            }
            
            size_t index = s.find(delim, start);
            
            while (index != std::string::npos) {
                ret.push_back(std::string(s.substr(start, index - start)));
                start = index + sub.size();
                index = s.find(delim, start);
            }
            
            size_t size1 = src.size();
            
            if (start < src.size()) {
                ret.push_back(std::string(s.substr(start)));
            }
            
            return ret;
        }
        
        static std::vector< std::string > split_regex(const std::string& src,
                const std::string& sub) {
            std::string s = src;
            std::string delim = sub;
            
            std::string str_reg = "^.+?" + sub;
            std::regex reg(str_reg);
            
            std::vector<std::string> ret;
            size_t start = 0;
            
            if (s.empty()) {
                ret.push_back("");
                return ret;
            }
            
            std::smatch m;
            std::vector<std::string> sources;
            
            
            while (std::regex_search(s, m, reg)) {
                std::string mstr = m[0].str();
                std::string item = mstr.substr(0, mstr.size() - sub.size());
                ret.push_back(item);
                
                s = s.substr(m.prefix().length() + m[0].length());
            }
            
            
            return ret;
        }
        
        static void replace(std::string& m_str, const std::string &strsrc,
                            const std::string &strdst) {
            if (strsrc.empty()) {
                return;
            }
            
            std::string::size_type pos = 0;
            std::string::size_type srclen = strsrc.size();
            std::string::size_type dstlen = strdst.size();
            
            while ((pos = m_str.find(strsrc, pos)) != std::string::npos) {
                m_str.replace(pos, srclen, strdst);
                pos += dstlen;
            }
            
            return;
        }
        
        
        
        static void replace(std::wstring& m_str, const std::wstring &strsrc,
                            const std::wstring &strdst) {
            if (strsrc.empty()) {
                return;
            }
            
            std::wstring::size_type pos = 0;
            std::wstring::size_type srclen = strsrc.size();
            std::wstring::size_type dstlen = strdst.size();
            
            while ((pos = m_str.find(strsrc, pos)) != std::wstring::npos) {
                m_str.replace(pos, srclen, strdst);
                pos += dstlen;
            }
            
            return;
        }
        
        
        static void replace_first(std::string& m_str, const std::string &strsrc,
                                  const std::string &strdst) {
            if (strsrc.empty()) {
                return;
            }
            
            std::string::size_type pos = 0;
            std::string::size_type srclen = strsrc.size();
            std::string::size_type dstlen = strdst.size();
            
            while ((pos = m_str.find(strsrc, pos)) != std::string::npos) {
                m_str.replace(pos, srclen, strdst);
                break;
            }
            
            return;
        }
        
        static void replace_first(std::wstring& m_str, const std::wstring &strsrc,
                                  const std::wstring &strdst) {
            if (strsrc.empty()) {
                return;
            }
            
            std::wstring::size_type pos = 0;
            std::wstring::size_type srclen = strsrc.size();
            std::wstring::size_type dstlen = strdst.size();
            
            while ((pos = m_str.find(strsrc, pos)) != std::wstring::npos) {
                m_str.replace(pos, srclen, strdst);
                break;
            }
            
            return;
        }
        static std::string replace_c(const std::string& m_str,
                                     const std::string &strsrc,
                                     const std::string &strdst) {
            std::string str = m_str;
            replace(str, strsrc, strdst);
            return str;
        }
        static std::wstring replace_c(const std::wstring& m_str,
                                      const std::wstring &strsrc,
                                      const std::wstring &strdst) {
            std::wstring str = m_str;
            replace(str, strsrc, strdst);
            return str;
        }
        
        static std::string replace_first_c(const std::string& m_str,
                                           const std::string &strsrc,
                                           const std::string &strdst) {
            std::string str = m_str;
            replace_first(str, strsrc, strdst);
            return str;
        }
        
        static std::wstring replace_first_c(const std::wstring& m_str,
                                            const std::wstring &strsrc,
                                            const std::wstring &strdst) {
            std::wstring str = m_str;
            replace_first_c(str, strsrc, strdst);
            return str;
        }
        
        static std::string mapValueToString(const
                                            std::unordered_map<std::string, std::string>& map, const std::string& key) {
            auto iter = map.find(key);
            
            if (iter == map.end()) {
                return "";
            }
            
            return iter->second;
        }
        
        static std::string format(const std::string& src,
                                  const std::unordered_map<std::string, std::string>& map) {
            std::regex args("\\{\\w+\\}");
            std::smatch m;
            std::string ss = src;
            std::string leftFlag = "--4652edc3-0bd2-4653-abbb-1eb8842da732--";
            std::string rightFlag = "--2d2fa095-cef4-4b9e-87f3-b9aeff613241 --";
            
            while (std::regex_search(ss, m, args)) {
                std::string key = m[0].str();
                replace(key, "{", "\\{");
                replace(key, "}", "\\}");
                
                std::regex e(key);
                replace(key, "\\{", "");
                replace(key, "\\}", "");
                
                std::string dststr = mapValueToString(map, key);
                amo::string_utils::replace(dststr, "{", leftFlag);
                amo::string_utils::replace(dststr, "}", rightFlag);
                ss = std::regex_replace(ss, e, dststr);
                /* ss = std::regex_replace(ss, e, mapValueToString(map, key));*/
            }
            
            amo::string_utils::replace(ss, leftFlag, "{");
            amo::string_utils::replace(ss, rightFlag, "}");
            return ss;
        }
        
        
        static  std::string& to_upper(std::string& m_str) {
            std::transform(m_str.begin(), m_str.end(), m_str.begin(), toupper);
            return m_str;
        }
        
        
        static std::string to_upper(const std::string& m_str) {
            std::string s(m_str);
            std::transform(m_str.begin(), m_str.end(), s.begin(), toupper);
            return s;
        }
        
        static std::string to_upper_copy(const std::string& m_str) {
            std::string s(m_str);
            std::transform(m_str.begin(), m_str.end(), s.begin(), toupper);
            return s;
            
        }
        
        static std::string& to_lower(std::string& m_str) {
            std::transform(m_str.begin(), m_str.end(), m_str.begin(), tolower);
            return m_str;
        }
        
        
        static  std::string to_lower(const std::string& m_str) {
            std::string s(m_str);
            std::transform(m_str.begin(), m_str.end(), s.begin(), tolower);
            return s;
        }
        
        static  std::string to_lower_copy(const std::string& m_str) {
            std::string s(m_str);
            std::transform(m_str.begin(), m_str.end(), s.begin(), tolower);
            return s;
        }
        
        /*!
        * @fn	static int compare(const std::string& a, const std::string& b, bool ignoreCase = false)
        *
        * @brief	比较两个字符串的大小.
        *
        * @param	a		  	Constant standard string&amp; to be compared.
        * @param	b		  	Constant standard string&amp; to be compared.
        * @param	ignoreCase	(Optional) bool to be compared.
        *
        * @return	Negative if 'a' is less than 'b', 0 if they are equal, or positive if it is greater.
        */
        
        static int compare(const std::string& a, const std::string& b,
                           bool ignoreCase = false) {
            std::string left = a;
            std::string right = b;
            
            if (ignoreCase) {
                amo::string_utils::to_upper(left);
                amo::string_utils::to_upper(right);
            }
            
            int len = (std::min)(left.size(), right.size());
            
            for (int i = 0; i < len; ++i) {
                if (left[i] < right[i]) {
                    return -1;
                }
                
                if (left[i] > right[i]) {
                    return 1;
                }
            }
            
            if (left.size() == right.size()) {
                return 0;
            }
            
            return left.size() < right.size();
        }
        
        // 移除字符串中的重复字符，可以指定字符
        static  std::string& unique(std::string& src,
                                    const std::string& target = " ") {
            auto iter = std::unique(src.begin(), src.end(), [&](char l, char r) {
                if (target.find(l) != std::string::npos
                        && target.find(r) != std::string::npos) {
                    return true;
                }
                
                return false;
            });
            
            src.erase(iter, src.end());
            
            return src;
        }
        
        static std::string unique_copy(std::string& src,
                                       const std::string& target = " ") {
            std::string val = src;
            unique(val, target);
            return val;
        }
        
        //删除左边所有包含在target中的字符
        static  std::string& trim(std::string& src, const std::string& target = " ") {
            replace(src, target, "");
            return src;
        }
        
        static std::string trim_c(const std::string& src,
                                  const std::string& target = " ") {
            std::string ret = src;
            replace(ret, target, "");
            return ret;
        }
        
        //删除左边所有包含在target中的字符
        static  std::string& trim_left(std::string& src,
                                       const std::string& target = " \t") {
            while (!src.empty() && (target.find(*src.begin()) != std::string::npos)) {
                src.erase(src.begin());
            }
            
            return src;
        }
        
        
        static  std::string trim_left_copy(const std::string& val,
                                           const std::string& target = " \t") {
            std::string src = val;
            
            while (!src.empty() && (target.find(*src.begin()) != std::string::npos)) {
                src.erase(src.begin());
            }
            
            return src;
        }
        
        // 删除左边所有字符，直到遇到所给的任意一字符为止
        static  std::string& trim_left_until(std::string& src,
                                             const std::string& target) {
            while (!src.empty() && (target.find(*src.begin()) == std::string::npos)) {
                src.erase(src.begin());
            }
            
            return src;
        }
        
        static  std::string& trim_left_until_copy(std::string& val,
                const std::string& target) {
            std::string src = val;
            trim_left_until(src, target);
            return src;
        }
        
        //删除右边所有包含在target中的字符
        static std::string& trim_right(std::string& src,
                                       const std::string& target = " \t") {
            while (!src.empty() && target.find(*src.rbegin()) != std::string::npos) {
                src.erase(--src.end());
            }
            
            return src;
        }
        
        static std::string trim_right_copy(const std::string& val,
                                           const std::string& target) {
            std::string src = val;
            
            while (!src.empty() && target.find(*src.rbegin()) != std::string::npos) {
                src.erase(--src.end());
            }
            
            return src;
        }
        
        // 删除右边所有字符，直到遇到 所给的任意一字符为止
        static  std::string& trim_right_until(std::string& src,
                                              const std::string& target) {
            while (!src.empty() && target.find(*src.rbegin()) == std::string::npos) {
                src.erase(--src.end());
            }
            
            return src;
        }
        
        static  std::string& trim_right_until_copy(std::string& val,
                const std::string& target) {
            std::string src = val;
            trim_right_until(src, target);
            return src;
        }
        
        
        //返回左边count个字符，count大于总长度则返回整个字符串
        static  std::string left(std::string& src, std::string::size_type count) {
            return src.substr(0, count);
        }
        
        //返回右边count个字符，count大于总长度则返回整个字符串
        static  std::string right(std::string& src, std::string::size_type count) {
            return src.substr(src.size() < count ? 0 : src.size() - count);
        }
        
        //忽略大小写判断两个字符串是否相等
        static int compare_no_case(const std::string& src, const std::string& rhs) {
            return to_lower(src).compare(to_lower(rhs));
        }
        
        
        //判断字符串是否以制定字符串开头
        static bool begin_with(const std::string& str, const std::string& rhs) {
            return str.find(rhs) == std::string::size_type(0);
        }
        
        //判断字符串是否以制定字符串结尾
        static bool end_with(const std::string& str, const std::string& rhs) {
            if (rhs.size() > str.size()) {
                return false;
            }
            
            return str.compare(str.size() - rhs.size(), rhs.size(), rhs) == 0;
        }
        
        //判断字符串是否以制定字符串开头
        static bool start_with(const std::string& str, const std::string& rhs) {
            return begin_with(str, rhs);
        }
        // algin 对齐方式 0 左对齐 1 居中 2 右对齐
        // min_len 字符串最小长度，如果长度不够，将被齐，转为为0 表示以所给字符串长度为
        
        /*static void make_same_length(std::vector<std::string>& vec, int align = 0,
                                     int min_len = 0, char fill_char = ' ') {
            std::vector<int> arr;
        
            for (auto& p : vec) {
                arr.push_back(p.size());
            }
        
            if (min_len > 0) {
                arr.push_back(min_len);
            }
        
            auto iter = std::max_element(arr.begin(), arr.end());
            int minLen = *iter;
        
        
        
            if (align == 0) {
                for (auto& p : vec) {
                    std::string right_fill_str = "";
                    int fill_len = minLen - p.size();
        
                    if (fill_len > 0) {
                        right_fill_str = std::string(fill_len, fill_char);
                    }
        
                    p = p + right_fill_str;
                }
        
        
        
            } else if (align == 1) {
                for (auto& p : vec) {
                    std::string left_fill_str = "";
                    std::string right_fill_str = "";
                    int fill_len = minLen - p.size();
        
                    if (fill_len > 0) {
                        int left_fill_len = fill_len / 2;
                        int right_fill_len = fill_len - left_fill_len;
                        left_fill_str = std::string(left_fill_len, fill_char);
                        right_fill_str = std::string(right_fill_len, fill_char);
                    }
        
                    p = left_fill_str + p + right_fill_str;
                }
            } else if (align == 2) {
                for (auto& p : vec) {
                    std::string left_fill_str = "";
                    std::string right_fill_str = "";
                    int fill_len = minLen - p.size();
        
                    if (fill_len > 0) {
                        left_fill_str = std::string(fill_len, fill_char);
                    }
        
                    p = left_fill_str + p;
                }
            }
        
            return;
        }*/
        
        
        
        static void make_same_length(std::vector<std::pair<std::string, int> >& vec,
                                     int align = 0,
                                     int min_len = 0, char fill_char = ' ') {
            std::vector<int> arr;
            
            for (auto& p : vec) {
                arr.push_back(p.second);
            }
            
            if (min_len > 0) {
                arr.push_back(min_len);
            }
            
            auto iter = std::max_element(arr.begin(), arr.end());
            int minLen = *iter;
            
            
            
            if (align == 0) {
                for (auto& p : vec) {
                    std::string right_fill_str = "";
                    int fill_len = minLen - p.second;
                    
                    if (fill_len > 0) {
                        right_fill_str = std::string(fill_len, fill_char);
                    }
                    
                    p.first = p.first + right_fill_str;
                }
                
                
                
            } else if (align == 1) {
                for (auto& p : vec) {
                    std::string left_fill_str = "";
                    std::string right_fill_str = "";
                    int fill_len = minLen - p.second;
                    
                    if (fill_len > 0) {
                        int left_fill_len = fill_len / 2;
                        int right_fill_len = fill_len - left_fill_len;
                        left_fill_str = std::string(left_fill_len, fill_char);
                        right_fill_str = std::string(right_fill_len, fill_char);
                    }
                    
                    p.first = left_fill_str + p.first + right_fill_str;
                }
            } else if (align == 2) {
                for (auto& p : vec) {
                    std::string left_fill_str = "";
                    std::string right_fill_str = "";
                    int fill_len = minLen - p.second;
                    
                    if (fill_len > 0) {
                        left_fill_str = std::string(fill_len, fill_char);
                    }
                    
                    p.first = left_fill_str + p.first;
                }
            }
            
            return;
        }
        
        static void make_same_length(std::function<int(const std::string&)> fn_get_len,
                                     std::vector<std::string>& vec, int align = 0,
                                     int min_len = 0, char fill_char = ' ') {
                                     
            std::vector<std::pair<std::string, int> > arr;
            
            
            for (auto& p : vec) {
                int len = p.size();
                
                if (fn_get_len) {
                    len = fn_get_len(p);
                }
                
                arr.push_back({ p, len });
            }
            
            make_same_length(arr, align, min_len, fill_char);
            
            for (size_t i = 0; i < arr.size(); ++i) {
                vec[i] = arr[i].first;
            }
            
            return;
        }
        
        /*static void make_same_length(std::function<int(const std::wstring&)>
                                     fn_callback,
                                     std::vector<std::wstring>& vec,
                                     int align = 0,
                                     int min_len = 0, wchar_t fill_char = L' ') {
            std::vector<int> arr;
        
            for (auto& p : vec) {
                if (fn_callback) {
                    arr.push_back(fn_callback(p));
                } else {
                    arr.push_back(p.size());
                }
            }
        
            if (min_len > 0) {
                arr.push_back(min_len);
            }
        
            auto iter = std::max_element(arr.begin(), arr.end());
            int minLen = *iter;
        
        
        
            if (align == 0) {
                for (auto& p : vec) {
                    std::wstring right_fill_str = L"";
                    int fill_len = minLen - p.size();
        
                    if (fill_len > 0) {
                        right_fill_str = std::wstring(fill_len, fill_char);
                    }
        
                    p = p + right_fill_str;
                }
        
        
        
            } else if (align == 1) {
                for (auto& p : vec) {
                    std::wstring left_fill_str = L"";
                    std::wstring right_fill_str = L"";
                    int fill_len = minLen - p.size();
        
                    if (fill_len > 0) {
                        int left_fill_len = fill_len / 2;
                        int right_fill_len = fill_len - left_fill_len;
                        left_fill_str = std::wstring(left_fill_len, fill_char);
                        right_fill_str = std::wstring(right_fill_len, fill_char);
                    }
        
                    p = left_fill_str + p + right_fill_str;
                }
            } else if (align == 2) {
                for (auto& p : vec) {
                    std::wstring left_fill_str = L"";
                    std::wstring right_fill_str = L"";
                    int fill_len = minLen - p.size();
        
                    if (fill_len > 0) {
                        left_fill_str = std::wstring(fill_len, fill_char);
                    }
        
                    p = left_fill_str + p;
                }
            }
        
            return;
        }*/
    }
}

#endif // AMO_STRING_ALGO_F04C5FE1_7DCD_4367_B76D_23905D0A7E27_HPP__

